Fold inherited rules and associations into the entity that inherits them - #18
Merged
Conversation
Folding properties carried what lives on a property and left everything recorded on the class behind. A RuleCriteria on an audit base is enforced every time any entity in the application is saved; an [Appearance] greys a field on every screen below it; an association gives every descendant a collection that really is populated. All three appeared under the base alone, so a reader told the inventories were complete read an entity's section and was told of no rule. Each folded declaration now names the class that wrote it -- in the entity's properties too, where InheritedFrom has been recorded since 0.13.0 and shown by nothing. Folding without marking would trade one defect for a quieter one: the reader sees every rule that governs the entity and cannot tell which of them changing would change the whole application. The other half of #14 is what an index, a count, a diagram or a search should say once every entity carries its ancestors' declarations. They are answering a different question -- what this application declares -- and following the fold there would make every one of them a measurement of the class hierarchy. One rule on a base shared by two hundred entities is one rule. So those read declarations; an entity's own section reads everything that governs it. Found while probing: the four-argument rule attribute -- [RuleCriteria("id", DefaultContexts.Save, "Total >= 0", "A sale total cannot be negative.")] -- put the message in the field holding what the rule enforces and left the message field empty. Every fixture passed its message as CustomMessageTemplate =, so all 299 tests agreed with the wrong answer. Fixed here rather than after, because the fold multiplies whatever a rule holds. A rule now also carries its identifier and its validation contexts, read as arg0 and arg1 and printed to the published documentation that way. Closes #14 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comment on lines
+1111
to
+1121
| foreach (var item in fromParent) | ||
| { | ||
| // Redeclaring wins: a descendant that reuses an identifier is replacing the rule, and | ||
| // listing both would show a reader two rules that contradict each other. | ||
| if (!own.Add(keyOf(item))) | ||
| continue; | ||
|
|
||
| var copy = clone(item); | ||
| markDeclarer(copy, parentClassName); | ||
| target.Add(copy); | ||
| } |
This was referenced Aug 17, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #14.
What was wrong
Folding inherited properties (#15) carried what lives on a property and left everything recorded on the class behind. Measured against the extended
AuditedXpoSolutionfixture,Receipthad no validation rules, no appearance rules and no relationships — while its own property table saidCreatedBywas required. The document contradicted itself on the same page.All three of those are real for the entity:
RuleCriteriaon an audit base is enforced every time any entity in the application is saved[Appearance]greys a field on every screen below itReceiptnow reads:Marking is not decoration. Folding without it trades one defect for a quieter one: the reader now sees every rule that governs the entity and cannot tell which of them changing would change the whole application.
InheritedFromhas been recorded on properties since 0.13.0 and displayed by nothing — the property table gained it here too.The debatable half
The issue asks that relationships be decided on purpose rather than by omission. They are, and so is everything else: an entity's own section reports what governs it; an index, a count, a diagram, a search and the diff report what the application declares.
One rule on a base shared by two hundred entities is one rule. Following the fold everywhere would make every total, map and search result a measurement of the class hierarchy — the same failure as #16, one collection over. On this fixture the declared count is 5 validation rules; the folded count is 12, on six entities, and it would grow when somebody adds a subclass and changes nothing about the validation.
Pinned in
DeclaredRatherThanFoldedTests. Declarer-marking is what lets each rendering choose.Found while probing
The four-argument rule attribute was misread:
PositionalCriteriatook the last positional literal as the criteria, which is right for the three-argument overload and wrong for this one — soExpressionheld the sentence explaining the rule andMessageTemplatestayed empty. Every fixture in the suite passed its message asCustomMessageTemplate =, so all 299 tests agreed with the wrong answer.Fixed here rather than in a follow-up, because the fold multiplies whatever a rule holds. A rule now also carries its identifier and its validation contexts, which were read into the raw parameter bag as
arg0andarg1— and printed to the published documentation that way:The enforced criteria was extracted in 0.13.0 and shown by neither the MCP detail tool nor the HTML page; both show it now.
Verification
AuditedXpoSolutiongained a rule, an appearance rule, an association and a third inheritance level, so a rule now has to arrive from a grandparent and a redeclaration has to win.TheDemoApplicationKeepsItsShapestill pins 24 relationships and 9 rules on the flat demo.